/*   
 * linux/arch/ckcore/kernel/setup.c
 * This file handles the architecture-dependent parts of system setup
 *
 * This file is subject to the terms and conditions of the GNU General Public
 * License.  See the file "COPYING" in the main directory of this archive
 * for more details.
 *
 * Copyright (C) 2006  Hangzhou C-SKY Microsystems co.,ltd.
 * Copyright (C) 2006  Li Chunqiang (chunqiang_li@c-sky.com)
 * Copyright (C) 2009  Hu junshan (junshan_hu@c-sky.com)  
 *
 */

#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/sched.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/fs.h>
#include <linux/console.h>
#include <linux/genhd.h>
#include <linux/errno.h>
#include <linux/string.h>
#include <linux/init.h>
#include <linux/bootmem.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/module.h>
#include <linux/initrd.h>
#include <linux/rtc.h>

#include <asm/bootinfo.h>
#include <asm/sections.h>
#include <asm/setup.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <asm/machdep.h>

#ifdef CONFIG_BLK_DEV_INITRD
#include <asm/pgtable.h>
#endif

#ifdef CONFIG_CONSOLE
extern struct consw *conswitchp;
#ifdef CONFIG_FRAMEBUFFER
extern struct consw fb_con;
#endif
#endif

unsigned long rom_length;
unsigned long memory_start;
unsigned long memory_end;

struct boot_mem_map boot_mem_map;

static char command_line[512] = "console=ttyS";

void (*mach_time_init) (irq_handler_t handler) __initdata = NULL;
void (*mach_tick)( void ) = NULL;
int (*mach_keyb_init) (void);
unsigned long (*mach_gettimeoffset) (void) = NULL;
int  (*mach_set_clock_mmss) (unsigned long) = NULL;
int (*mach_hwclk) (int, struct rtc_time*) = NULL;
EXPORT_SYMBOL(mach_hwclk);
unsigned int (*mach_get_ss)(void);
int (*mach_get_rtc_pll)(struct rtc_pll_info *);
int (*mach_set_rtc_pll)(struct rtc_pll_info *);
EXPORT_SYMBOL(mach_get_ss);
EXPORT_SYMBOL(mach_get_rtc_pll);
EXPORT_SYMBOL(mach_set_rtc_pll);
void (*mach_trap_init) (void) = NULL;
void (*mach_init_IRQ) (void) = NULL;
void (*mach_init_FIQ) (void) = NULL;
unsigned int (*mach_get_auto_irqno) (void) = NULL;
void (*mach_get_model) (char *model);
void (*mach_get_hardware_list) (struct seq_file *m);
void (*mach_reset)( void ) = NULL;
void (*mach_halt)( void ) = NULL;
void (*mach_power_off)( void ) = NULL;
#ifdef CONFIG_HEARTBEAT
void (*mach_heartbeat) (int);
EXPORT_SYMBOL(mach_heartbeat);
#endif

#ifdef CONFIG_CPU_CKCORE_MMU
	#define CPU "CK610"
#endif

extern unsigned int phy_ramstart, phy_ramend;

void __init add_memory_region(phys_t start, phys_t size,
			      long type)
{
	int x = boot_mem_map.nr_map;

	if (x == BOOT_MEM_MAP_MAX) {
		printk("Ooops! Too many entries in the memory map!\n");
		return;
	}

	boot_mem_map.map[x].addr = start;
	boot_mem_map.map[x].size = size;
	boot_mem_map.map[x].type = type;
	boot_mem_map.nr_map++;
}

static void __init print_memory_map(void)
{
	int i;

	for (i = 0; i < boot_mem_map.nr_map; i++) {
		printk(" memory: %08Lx @ %08Lx ",
			  (unsigned long long) boot_mem_map.map[i].size,
		          (unsigned long long) boot_mem_map.map[i].addr);

		switch (boot_mem_map.map[i].type) {
		case BOOT_MEM_RAM:
			printk("(usable)\n");
			break;
		case BOOT_MEM_ROM_DATA:
			printk("(ROM data)\n");
			break;
		case BOOT_MEM_RESERVED:
			printk("(reserved)\n");
			break;
		default:
			printk("type %lu\n", boot_mem_map.map[i].type);
			break;
		}
	}
}

static inline void parse_mem_cmdline(void)
{
	char c = ' ', *to = command_line, *from = saved_command_line;
	unsigned long start_at, mem_size;
	int len = 0;
	int usermem = 0;

	printk("Determined physical RAM map:\n");

	for (;;) {
		/*
		 * "mem=XXX[kKmM]" defines a memory region from
		 * 0 to <XXX>, overriding the determined size.
		 * "mem=XXX[KkmM]@YYY[KkmM]" defines a memory region from
		 * <YYY> to <YYY>+<XXX>, overriding the determined size.
		 */
		if (c == ' ' && !memcmp(from, "mem=", 4)) {
			if (to != command_line)
				to--;
			/*
			 * If a user specifies memory size, we
			 * blow away any automatically generated
			 * size.
			 */
			if (usermem == 0) {
				boot_mem_map.nr_map = 0;
				usermem = 1;
			}
			mem_size = memparse(from + 4, &from);
			if (*from == '@')
				start_at = memparse(from + 1, &from);
			else
				start_at = 0;
			add_memory_region(start_at, mem_size, BOOT_MEM_RAM);
		}
		c = *(from++);
		if (!c)
			break;
		if (CL_SIZE <= ++len)
			break;
		*(to++) = c;
	}
	*to = '\0';

	if (usermem) {
		printk("User-defined physical RAM map:\n");
		print_memory_map();
	}
}
static unsigned long __init init_initrd(void)
{
        return 0;
}

#define finalize_initrd()       do {} while (0)

/*
 * Initialize the bootmem allocator. It also setup initrd related data
 * if needed.
 */
static void __init bootmem_init(void)
{
        unsigned long reserved_end;
        unsigned long mapstart = ~0UL;
        unsigned long bootmap_size;
        int i;

        /*
         * Init any data related to initrd. It's a nop if INITRD is
         * not selected. Once that done we can determine the low bound
         * of usable memory.
         */
        reserved_end = max(init_initrd(),
                           (unsigned long) PFN_UP(__pa_symbol(&_end)));

        /*
         * max_low_pfn is not a number of pages. The number of pages
         * of the system is given by 'max_low_pfn - min_low_pfn'.
         */
        min_low_pfn = ~0UL;
        max_low_pfn = 0;

        /*
         * Find the highest page frame number we have available.
         */
        for (i = 0; i < boot_mem_map.nr_map; i++) {
                unsigned long start, end;

                if (boot_mem_map.map[i].type != BOOT_MEM_RAM)
                        continue;

                start = PFN_UP(boot_mem_map.map[i].addr);
                end = PFN_DOWN(boot_mem_map.map[i].addr
                                + boot_mem_map.map[i].size);

                if (end > max_low_pfn)
                        max_low_pfn = end;
                if (start < min_low_pfn)
                        min_low_pfn = start;
                if (end <= reserved_end)
                        continue;
                if (start >= mapstart)
                        continue;
                mapstart = max(reserved_end, start);
        }

        if (min_low_pfn >= max_low_pfn)
                panic("Incorrect memory mapping !!!");
        if (min_low_pfn > ARCH_PFN_OFFSET) {
                pr_info("Wasting %lu bytes for tracking %lu unused pages\n",
                        (min_low_pfn - ARCH_PFN_OFFSET) * sizeof(struct page),
                        min_low_pfn - ARCH_PFN_OFFSET);
        } else if (min_low_pfn < ARCH_PFN_OFFSET) {
                pr_info("%lu free pages won't be used\n",
                        ARCH_PFN_OFFSET - min_low_pfn);
        }
        min_low_pfn = ARCH_PFN_OFFSET;

        /*
         * Determine low and high memory ranges
         */
        max_pfn = max_low_pfn;
        if (max_low_pfn > PFN_DOWN(HIGHMEM_START)) {
#ifdef CONFIG_HIGHMEM
                highstart_pfn = PFN_DOWN(HIGHMEM_START);
                highend_pfn = max_low_pfn;
#endif
                max_low_pfn = PFN_DOWN(HIGHMEM_START);
        }

        /*
         * Initialize the boot-time allocator with low memory only.
         */
        bootmap_size = init_bootmem_node(NODE_DATA(0), mapstart,
                                         min_low_pfn, max_low_pfn);


        for (i = 0; i < boot_mem_map.nr_map; i++) {
                unsigned long start, end;

                start = PFN_UP(boot_mem_map.map[i].addr);
                end = PFN_DOWN(boot_mem_map.map[i].addr
                                + boot_mem_map.map[i].size);

                if (start <= min_low_pfn)
                        start = min_low_pfn;
                if (start >= end)
                        continue;
#ifndef CONFIG_HIGHMEM
                if (end > max_low_pfn)
                        end = max_low_pfn;

                /*
                 * ... finally, is the area going away?
                 */
                if (end <= start)
                        continue;
#endif

                add_active_range(0, start, end);
        }

        /*
         * Register fully available low RAM pages with the bootmem allocator.
         */ 
        for (i = 0; i < boot_mem_map.nr_map; i++) {
                unsigned long start, end, size;

                /*
                 * Reserve usable memory.
                 */
                if (boot_mem_map.map[i].type != BOOT_MEM_RAM)
                        continue;

                start = PFN_UP(boot_mem_map.map[i].addr);
                end   = PFN_DOWN(boot_mem_map.map[i].addr
                                    + boot_mem_map.map[i].size);
                /*
                 * We are rounding up the start address of usable memory
                 * and at the end of the usable range downwards.
                 */
                if (start >= max_low_pfn)
                        continue;
                if (start < reserved_end)
                        start = reserved_end;
                if (end > max_low_pfn)
                        end = max_low_pfn;


                /*
                 * ... finally, is the area going away?
                 */
                if (end <= start)
                        continue;
                size = end - start;

                /* Register lowmem ranges */
                free_bootmem(PFN_PHYS(start), size << PAGE_SHIFT);
                memory_present(0, start, end);
        }

        /*
         * Reserve the bootmap memory.
         */
        reserve_bootmem(PFN_PHYS(mapstart), bootmap_size, BOOTMEM_DEFAULT);
}

void __init setup_arch(char **cmdline_p)
{

//	ckcore_tlb_init();  //called by trap init 

	memory_start = PAGE_ALIGN(phy_ramstart);
	memory_end = phy_ramend; /* by now the stack is part of the init task */

	init_mm.start_code = (unsigned long) &_stext;
	init_mm.end_code = (unsigned long) &_etext;
	init_mm.end_data = (unsigned long) &_edata;
	init_mm.brk = (unsigned long) 0;

	config_BSP(&command_line[0], sizeof(command_line));

	printk("\r\nlinux/" CPU "\n");

#ifdef CONFIG_CPU_CKCORE_MMU
	printk("CKCORE port done by C-SKY Microsystems co.,ltd.  www.c-sky.com\n");
#if CONFIG_CK6408EVB
	printk("C-SKY CK6408EVB Board port by Hu junshan,  junshan_hu@c-sky.com\n");
#endif
#endif

#ifdef DEBUG
	printk("KERNEL -> TEXT=0x%06x-0x%06x DATA=0x%06x-0x%06x "
		"BSS=0x%06x-0x%06x\n", (int) &_stext, (int) &_etext,
		(int) &_sdata, (int) &_edata,
		(int) &_sbss, (int) &_ebss);
	printk("KERNEL -> ROMFS=0x%06x-0x%06x MEM=0x%06x-0x%06x "
		"STACK=0x%06x-0x%06x\n",
		(int) &_ebss, (int) memory_start,
		(int) memory_start, (int) memory_end,
		(int) memory_end, (int) phy_ramend);
#endif

#ifdef CONFIG_BLK_DEV_BLKMEM
	ROOT_DEV = BLKMEM_MAJOR;
	ROOT_DEV <<= MINORBITS;
#endif

	/* Keep a copy of command line */
	*cmdline_p=command_line;
	memcpy(boot_command_line, *cmdline_p, 256);

#ifdef DEBUG
	if (strlen(*cmdline_p)) 
		printk("Command line: '%s'\n", *cmdline_p);
#endif
	
#ifdef CONFIG_CONSOLE
#ifdef CONFIG_FRAMEBUFFER
	conswitchp = &fb_con;
#else
	conswitchp = 0;
#endif
#endif
	add_memory_region(memory_start,memory_end-memory_start,BOOT_MEM_RAM);

	bootmem_init();  //setup the bitmap for all the whole ram
	/*
	 * get kmalloc into gear
	 */
	paging_init();

#ifdef CONFIG_BLK_DEV_INITRD

	if (CONFIG_INITRAMFS_SOURCE == "") { 
#if (defined (CONFIG_ROOTFS_INITRD) && defined (CONFIG_INITRD_START_ADDR))
		reserve_bootmem(__pa(CONFIG_INITRD_START_ADDR),
			 CONFIG_INITRD_SIZE, BOOTMEM_DEFAULT);
		initrd_start = CONFIG_INITRD_START_ADDR; 
		initrd_end = initrd_start + CONFIG_INITRD_SIZE; 
		printk("initrd: %08lx - %08lx\n", initrd_start, initrd_end);
#endif
	}

#endif

#ifdef CONFIG_CPU_HAS_FPU
	init_fpu();
#endif

#ifdef DEBUG
	printk("Done setup_arch\n");
#endif

}

/*
 *	Get CPU information for use by the procfs.
 */

static int show_cpuinfo(struct seq_file *m, void *v)
{
    char *cpu, *mmu, *fpu;
    u_long clockfreq;

    cpu = CPU;
    mmu = "none";
    fpu = "none";

    clockfreq = (loops_per_jiffy*HZ)*16;

    seq_printf(m, "CPU:\t\t%s\n"
		   "MMU:\t\t%s\n"
		   "FPU:\t\t%s\n"
		   "Clocking:\t%lu.%1luMHz\n"
		   "BogoMips:\t%lu.%02lu\n"
		   "Calibration:\t%lu loops\n",
		   cpu, mmu, fpu,
		   clockfreq / 1000000, (clockfreq / 100000) % 10,
		   (loops_per_jiffy * HZ) / 500000, ((loops_per_jiffy * HZ) / 5000) % 100,
		   (loops_per_jiffy * HZ));

	return 0;
}

static void *c_start(struct seq_file *m, loff_t *pos)
{
	return *pos < NR_CPUS ? ((void *) 0x12345678) : NULL;
}

static void *c_next(struct seq_file *m, void *v, loff_t *pos)
{
	++*pos;
	return c_start(m, pos);
}

static void c_stop(struct seq_file *m, void *v)
{
}

struct seq_operations cpuinfo_op = {
	start:	c_start,
	next:	c_next,
	stop:	c_stop,
	show:	show_cpuinfo,
};

